package com.ruoyi.workflow.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.workflow.constant.MultiInstanceActivityConstants;
import com.ruoyi.workflow.enums.FlowInstanceStateEnum;
import com.ruoyi.workflow.enums.HandleActionEnum;
import com.ruoyi.workflow.exception.FlowModelException;
import com.ruoyi.workflow.exception.FlowTaskException;
import com.ruoyi.workflow.mapper.TaskMapper;
import com.ruoyi.workflow.pojo.entity.FlowInstance;
import com.ruoyi.workflow.pojo.entity.PearlFlowRecord;
import com.ruoyi.workflow.pojo.param.TaskHandleParams;
import com.ruoyi.workflow.pojo.query.MyTodoTaskQuery;
import com.ruoyi.workflow.pojo.vo.ActivityCompleteStatusVO;
import com.ruoyi.workflow.pojo.vo.MyTodoTaskVO;
import com.ruoyi.workflow.service.IFlowInstanceService;
import com.ruoyi.workflow.service.IFlowTaskService;
import com.ruoyi.workflow.service.IPearlFlowRecordService;
import org.apache.commons.io.IOUtils;
import org.camunda.bpm.engine.*;
import org.camunda.bpm.engine.history.HistoricActivityInstance;
import org.camunda.bpm.engine.repository.ProcessDefinition;
import org.camunda.bpm.engine.runtime.Execution;
import org.camunda.bpm.engine.task.DelegationState;
import org.camunda.bpm.engine.task.Task;
import org.camunda.bpm.model.bpmn.BpmnModelInstance;
import org.camunda.bpm.model.bpmn.instance.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * @author TD
 * @version 1.0
 * @date 2023/12/19
 */
@Service
public class FlowTaskServiceImpl implements IFlowTaskService {

    @Autowired
    TaskService taskService;

    @Autowired
    TaskMapper taskMapper;

    @Autowired
    HistoryService historyService;

    @Autowired
    RuntimeService runtimeService;

    @Autowired
    RepositoryService repositoryService;

    @Autowired
    IFlowInstanceService flowInstanceService;

    @Autowired
    IPearlFlowRecordService flowRecordService;

    @Override
    public List<MyTodoTaskVO> myTodo(MyTodoTaskQuery query) {
        Long userId = SecurityUtils.getUserId();
        // API查询
        taskService.createTaskQuery().taskAssignee(String.valueOf(userId)).list();
        // 自定义查询
        query.setAssignee(String.valueOf(userId));
        return taskMapper.selectMyTodoTask(query);
    }

    @Override
    public List<MyTodoTaskVO> myCompleted(MyTodoTaskQuery query) {
        // API 查询： SQL： select distinct RES.* from ACT_HI_TASKINST RES WHERE ( 1 = 1 and RES.ASSIGNEE_ = ? and RES.END_TIME_ is not null ) order by RES.ID_ asc LIMIT ? OFFSET ?
/*        HistoricTaskInstanceQuery finished = historyService.createHistoricTaskInstanceQuery().taskAssignee("1").finished();
        List<HistoricTaskInstance> taskInstanceList = finished.list();*/
        Long userId = SecurityUtils.getUserId();
        query.setAssignee(String.valueOf(userId));
        return taskMapper.selectMyCompleted(query);
    }

    @Override
    public void agree(TaskHandleParams params) {
        // 1. 校验
        // 任务是否存在
        String taskId = params.getTaskId();
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (Objects.isNull(task)) {
            throw new FlowTaskException("任务不存在");
        }
        // 审批人是否匹配
        String assignee = task.getAssignee();
        String userId = String.valueOf(SecurityUtils.getUserId());
        if (!userId.equals(assignee)) {
            throw new FlowTaskException("只能审批自己的任务");
        }

        // 是否委派
        DelegationState delegationState = task.getDelegationState();
        if (ObjectUtil.isNotNull(delegationState) && delegationState.equals(DelegationState.PENDING)) {
            // 2. 完成委托的任务
            taskService.resolveTask(taskId);
        } else {
            // 审批意见
            // taskService.createComment(taskId, task.getProcessInstanceId(), params.getComment());// (String taskId, String processInstance, String message
            // 3. 完成任务
            taskService.complete(taskId);
        }

        // 4. 新增流转记录
        String processInstanceId = task.getProcessInstanceId();
        FlowInstance flowInstance = flowInstanceService.selectFlowByInstanceId(processInstanceId);
        PearlFlowRecord record = new PearlFlowRecord();
        record.setInsId(flowInstance.getId());
        record.setHandlerUserName(SecurityUtils.getUsername());
        record.setHandlerAction(HandleActionEnum.AGREE.getMsg());
        record.setComment(params.getComment());
        record.setTaskName(task.getName());
        flowRecordService.insertPearlFlowRecord(record);
    }

    @Override
    @Transactional
    public void refuse(TaskHandleParams params) {

        // 1. 校验任务是否存在
        String taskId = params.getTaskId();
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (Objects.isNull(task)) {
            throw new FlowTaskException("未查询到任务");
        }

        // 2. 获取任务模型
        BpmnModelInstance bpmnModel = repositoryService.getBpmnModelInstance(task.getProcessDefinitionId());
        UserTask userTask = bpmnModel.getModelElementById(task.getTaskDefinitionKey());
        Collection<SequenceFlow> outgoing = userTask.getOutgoing(); // 流出

        // 3. 判断是否是多实例
        LoopCharacteristics loopCharacteristics = userTask.getLoopCharacteristics();
        if (ObjectUtil.isNotNull(loopCharacteristics)) {
            // 3.1 多实例

            // 获取完成表达式
            CompletionCondition completionCondition = (CompletionCondition) loopCharacteristics.getUniqueChildElementByType(CompletionCondition.class);
            String conditionTextContent = completionCondition.getTextContent();

            // 直接用表达式判断是那种类型（实际开发，可以存入到 UserTask）
            if (MultiInstanceActivityConstants.COMPLETE_CONDITION_HUI_QIAN_ALL_PASSED.equals(conditionTextContent)) {
                // 会签（需全部通过）
                // 直接完成并结束流程
                taskService.createComment(taskId, task.getProcessInstanceId(), params.getComment());// (String taskId, String processInstance, String message
                taskService.complete(taskId);
                endProcessIns(bpmnModel, task);
            } else if (MultiInstanceActivityConstants.COMPLETE_CONDITION_HUI_QIAN_HALF.equals(conditionTextContent)) {
                // TODO 暂时未做
                Integer nrOfInstances = (Integer) taskService.getVariable(task.getId(), MultiInstanceActivityConstants.NUMBER_OF_INSTANCES); // 实例总数
                Integer nrOfActiveInstances = (Integer) taskService.getVariable(task.getId(), MultiInstanceActivityConstants.NUMBER_OF_ACTIVE_INSTANCES);// 当前未完成的实例数量
                taskService.getVariableLocalTyped(taskId, "nrOfInstances");
                Map<String, Object> variablesLocal = taskService.getVariablesLocal(MultiInstanceActivityConstants.NUMBER_OF_INSTANCES);
                // 会签（半数通过）
            } else if (MultiInstanceActivityConstants.COMPLETE_CONDITION_HUO_QIAN.equals(conditionTextContent)) {
                // 或签（一个通过即可）
                Integer nrOfActiveInstances = (Integer) taskService.getVariable(task.getId(), MultiInstanceActivityConstants.NUMBER_OF_ACTIVE_INSTANCES);// 当前未完成的实例数量
                taskService.createComment(taskId, task.getProcessInstanceId(), params.getComment());// (String taskId, String processInstance, String message
                taskService.complete(taskId);// (String taskId, String processInstance, String message
                if (nrOfActiveInstances <= 1) { // 最后一票 结束流程
                    endProcessIns(bpmnModel, task);
                }
            } else {
                throw new FlowModelException("表达式有误");
            }
            //condition.getAttributeValueNs(BpmnModelConstants.CAMUNDA_NS,"completionCondition");
            Map<String, Object> variables = runtimeService.getVariables(task.getProcessInstanceId());
        } else {
            // 3.2 单实例

            // 完成任务
            taskService.createComment(taskId, task.getProcessInstanceId(), params.getComment());// (String taskId, String processInstance, String message
            taskService.complete(taskId);
            // 结束流程（流转到结束节点）
            endProcessIns(bpmnModel, task);
        }
        // 4. 新增流转记录
        String processInstanceId = task.getProcessInstanceId();
        FlowInstance flowInstance = flowInstanceService.selectFlowByInstanceId(processInstanceId);
        PearlFlowRecord record = new PearlFlowRecord();
        record.setInsId(flowInstance.getId());
        record.setHandlerUserName(SecurityUtils.getUsername());
        record.setHandlerAction(HandleActionEnum.REFUSE.getMsg());
        record.setComment(params.getComment());
        record.setTaskName(task.getName());
        flowRecordService.insertPearlFlowRecord(record);
    }

    @Override
    public void transfer(TaskHandleParams params) {
        // 1. 校验任务是否存在
        String taskId = params.getTaskId();
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (Objects.isNull(task)) {
            throw new FlowTaskException("未查询到任务");
        }

        // 2. 重新设置任务处理人
        String transferUserId = params.getTransferUserId();
        if (StrUtil.isEmpty(transferUserId)) {
            throw new FlowTaskException("转办人不能为空");
        }
        taskService.setAssignee(task.getId(), transferUserId);

        // 3. 新增流转记录
        PearlFlowRecord record = new PearlFlowRecord();
/*        record.setProcessInstanceId(task.getProcessInstanceId());*/
        record.setHandlerUserName(SecurityUtils.getUsername());
        record.setHandlerAction(HandleActionEnum.TRANSFER.getMsg());
        record.setComment(params.getComment());
        record.setTaskName(task.getName());
        flowRecordService.insertPearlFlowRecord(record);
    }

    @Override
    public void delegate(TaskHandleParams params) {
        // 1. 校验任务是否存在
        String taskId = params.getTaskId();
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (Objects.isNull(task)) {
            throw new FlowTaskException("未查询到任务");
        }

        // 2. 委派
        String delegateUserId = params.getDelegateUserId();
        if (StrUtil.isEmpty(delegateUserId)) {
            throw new FlowTaskException("转办人不能为空");
        }
        taskService.delegateTask(task.getId(), delegateUserId);// 委派

        // 3. 新增流转记录
        PearlFlowRecord record = new PearlFlowRecord();
/*        record.setInsId(task.getProcessInstanceId());*/
        record.setHandlerUserName(SecurityUtils.getUsername());
        record.setHandlerAction(HandleActionEnum.DELEGATE.getMsg());
        record.setComment(params.getComment());
        record.setTaskName(task.getName());
        flowRecordService.insertPearlFlowRecord(record);
    }

    private void endProcessIns(BpmnModelInstance bpmnModel, Task task) {
        Collection<EndEvent> endEvents = bpmnModel.getModelElementsByType(EndEvent.class);
        EndEvent endEvent = endEvents.stream().findFirst().get(); // 第一个结束节点
        List<Execution> executionList = runtimeService.createExecutionQuery() // 所有正在执行的节点
                .processInstanceId(task.getProcessInstanceId())
                .active()
                .list();
        if (CollUtil.isNotEmpty(executionList)) { // 所有正在执行的节点直接调转到结束节点
            for (Execution execution : executionList) {
                runtimeService.createModification(task.getProcessDefinitionId())
                        .processInstanceIds(task.getProcessInstanceId())
                        .cancelAllForActivity(execution.getId())
                        .startBeforeActivity(endEvent.getId())
                        .execute();
            }
        }
        // 更新自定义例程实例表
        FlowInstance flowInstance = flowInstanceService.selectFlowByInstanceId(task.getProcessInstanceId());
        flowInstance.setUpdateBy(SecurityUtils.getUsername());
        flowInstance.setUpdateTime(new Date());
        flowInstance.setState(FlowInstanceStateEnum.REJECTED.getCode());
        flowInstanceService.updateFlowInstance(flowInstance);
    }
}
